package com.resrun.service.cert;

import com.resrun.dao.CACertDao;
import com.resrun.dao.CAKeysDao;
import com.resrun.dao.CARequestDao;
import com.resrun.dto.CertGenDto;
import com.resrun.dto.GenerateCertDto;
import com.resrun.enums.CertFormatEnum;
import com.resrun.enums.CertStateEnum;
import com.resrun.enums.CertTypeEnum;
import com.resrun.enums.RequestStateEnum;
import com.resrun.model.CACert;
import com.resrun.model.CAKeys;
import com.resrun.model.CARequest;
import com.resrun.service.pojo.CertificateInfo;
import com.resrun.utils.CaUtil;
import com.resrun.utils.KeyUtil;
import com.resrun.vo.base.Result;
import com.resrun.vo.request.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.CertIOException;
import org.bouncycastle.operator.OperatorCreationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@AllArgsConstructor
public class CAService {

    private CARequestDao requestDao;
    private CACertDao certDao;
    private CAKeysDao keysDao;

    /**
     * 根据ID查询申请记录
     *
     * @param requestId 申请记录ID
     * @return Result
     */
    public Result<CARequest> getRequestById(Long requestId) {
        if (requestId == null) {
            return Result.error("请求参数不能为空");
        }

        return Result.ok(requestDao.getById(requestId));
    }

    /**
     * 证书申请
     *
     * @param request 申请参数
     * @return Result
     */
    public Result<Void> saveRequest(CARequestRequest request) {
        if (request == null) {
            return Result.error("请求参数不能为空");
        }

        CertTypeEnum certTypeEnum = CertTypeEnum.of(request.getCertType());
        if (certTypeEnum == null) {
            return Result.error("证书类型不正确");
        }

        if (certTypeEnum == CertTypeEnum.PERSONAL && request.getUserId() == null) {
            return Result.error("用户ID不能为空");
        }

        if (certTypeEnum == CertTypeEnum.ENTERPRISE && request.getEnterpriseId() == null) {
            return Result.error("企业ID不能为空");
        }

        requestDao.saveRequest(CARequest.of(request));
        return Result.ok();
    }

    public Result<Void> audit(CAAuditRequest request) {
        if (request == null) {
            return Result.error("请求参数不能为空");
        }

        requestDao.updateRequest(CARequest.of(request));
        return Result.ok();
    }

    public Result<Void> generate(Long requestId) {
        CARequest request = requestDao.getById(requestId);
        if (request == null) {
            return Result.error("申请记录不存在");
        }

        X500Name x500Name = CaUtil.generateX500Name(request);
        KeyPair keyPair = KeyUtil.generateKeyPair();
        CertGenDto certGenDto = new CertGenDto();
        certGenDto.setX500Name(x500Name);
        certGenDto.setKeyPair(keyPair);
        certGenDto.setDuration(TimeUnit.DAYS.toSeconds(365));

        try {
            X509Certificate x509Certificate = CaUtil.generateCACertificate(certGenDto);
            byte[] encoded = x509Certificate.getEncoded();
            CACertRequest cert = new CACertRequest();
            cert.setRequestId(requestId);
            cert.setEncode(encoded);
            cert.setEffectiveDate(x509Certificate.getNotBefore());
            cert.setExpirationDate(x509Certificate.getNotAfter());
            cert.setState(CertStateEnum.INIT.getState());
            certDao.insert(CACert.of(cert));
            CAKeysRequest keys = new CAKeysRequest();
            keys.setRequestId(requestId);
            keys.setPrivateKeyEncode(keyPair.getPrivate().getEncoded());
            keys.setPublicKeyEncode(x509Certificate.getPublicKey().getEncoded());
            keysDao.saveKeys(CAKeys.of(keys));
        } catch (CertificateException | OperatorCreationException | CertIOException e) {
            log.error("证书生成异常", e);
            return Result.error("证书生成异常");
        }

        return Result.ok();
    }

    @Transactional(rollbackFor = Exception.class)
    public Result<Void> generate(GenerateCertRequest request) {
        CARequest caRequest = requestDao.getById(request.getRequestId());
        if (caRequest == null) {
            return Result.error("查询申请记录为空");
        }

        if (caRequest.getState() != RequestStateEnum.PASS.getState()) {
            return Result.error("证书申请审核未通过");
        }

        CACert caCert = certDao.getByRequestId(caRequest.getId());
        if (caCert != null && caCert.getState() == CertStateEnum.INIT.getState()) {
            return Result.error("证书已存在");
        }

        try {
            String subject = CaUtil.generateSubject(caRequest);
            GenerateCertDto generateCertDto = new GenerateCertDto();
            generateCertDto.setSubject(subject);
            generateCertDto.setAlias(request.getAlias());
            generateCertDto.setPassowrd(request.getPassowrd());
            generateCertDto.setLifespan((byte) 1);
            CertificateInfo certificateInfo = CaUtil.generateCertificate(null, generateCertDto);

            // 将密钥对保存到数据库
            CAKeys keys = new CAKeys();
            keys.setRequestId(request.getRequestId());
            keys.setPrivateKeyEncode(certificateInfo.getPrivateKey());
            keys.setPublicKeyEncode(certificateInfo.getPublicKey());
            keysDao.saveKeys(keys);

            // 将证书信息保存到数据库
            CACert cert = new CACert();
            cert.setRequestId(request.getRequestId());
            cert.setAlias(certificateInfo.getAlias());
            cert.setSerial(certificateInfo.getSerial());
            cert.setFormat(CertFormatEnum.PKCS12.getName());
            cert.setPassword(certificateInfo.getPassword());
            cert.setEncode(certificateInfo.getPfx());
            cert.setEffectiveDate(certificateInfo.getTermOfValidityStartTime());
            cert.setExpirationDate(certificateInfo.getTermOfValidityEndTime());
            cert.setState(CertStateEnum.INIT.getState());
            certDao.insertSelective(cert);

            return Result.ok();
        } catch (GeneralSecurityException | IOException | OperatorCreationException e) {
            log.error("证书生成异常", e);
            return Result.error("证书生成异常");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Result<Void> regenerate(ReGenerateCertRequest request) {
        CARequest caRequest = requestDao.getById(request.getRequestId());
        if (caRequest == null) {
            return Result.error("查询申请记录为空");
        }

        if (caRequest.getState() != RequestStateEnum.PASS.getState()) {
            return Result.error("证书申请审核未通过");
        }

        try {
            String subject = CaUtil.generateSubject(caRequest);
            GenerateCertDto generateCertDto = new GenerateCertDto();
            generateCertDto.setSubject(subject);
            generateCertDto.setAlias(request.getAlias());
            generateCertDto.setPassowrd(request.getPassowrd());
            generateCertDto.setLifespan((byte) 1);
            CertificateInfo certificateInfo = CaUtil.generateCertificate(null, generateCertDto);

            // 将密钥对保存到数据库
            CAKeys keys = new CAKeys();
            keys.setRequestId(request.getRequestId());
            keys.setPrivateKeyEncode(certificateInfo.getPrivateKey());
            keys.setPublicKeyEncode(certificateInfo.getPublicKey());
            keysDao.updateKeys(keys);

            // 将证书信息保存到数据库
            CACert cert = new CACert();
            cert.setRequestId(request.getRequestId());
            cert.setAlias(certificateInfo.getAlias());
            cert.setSerial(certificateInfo.getSerial());
            cert.setFormat(CertFormatEnum.PKCS12.getName());
            cert.setPassword(certificateInfo.getPassword());
            cert.setEncode(certificateInfo.getPfx());
            cert.setEffectiveDate(certificateInfo.getTermOfValidityStartTime());
            cert.setExpirationDate(certificateInfo.getTermOfValidityEndTime());
            cert.setState(CertStateEnum.INIT.getState());
            certDao.updateCert(cert);

            return Result.ok();
        } catch (GeneralSecurityException | IOException | OperatorCreationException e) {
            log.error("重新生成证书异常", e);
            return Result.error("重新生成证书异常");
        }
    }

    public Result<Void> saveCert(CACert cert) {
        certDao.insertSelective(cert);
        return Result.ok();
    }

    public Result<CACert> getCertById(Long certId) {
        if (certId == null) {
            Result.error("请求参数不能为空");
        }

        CACert cert = certDao.getById(certId);
        if (cert == null) {
            return Result.error("证书不存在");
        }

        return Result.ok(cert);
    }

    public CARequest getLatestByUserId(Long userId) {
        if (userId == null) {
            Result.error("请求参数不能为空");
        }

        return requestDao.getLatestByUserId(userId);
    }

    public CACert getLatestCertByUserId(Long userId) {
        return certDao.getLatestCertByUserId(userId);
    }

    public CACert getLatestCertByEnterpriseId(Long enterpriseId) {
        return certDao.getLatestCertByEnterpriseId(enterpriseId);
    }
}
